//
//  GZSQLModelTool.m
//  GZSQLModelTool_Example
//
//  Created by Guo Rui on 2018/1/25.
//  Copyright © 2018年 GR. All rights reserved.
//

#import "GZSQLModelTool.h"
#import "GZSQLTool.h"
#import "GZSQLModelResolver.h"
#import "GZSQLTableResolver.h"

@implementation GZSQLModelTool

+ (BOOL)createTable:(Class)cls uid:(NSString *)uid
{
    /**
     CREATE TABLE IF NOT EXISTS t_student (
     id integer PRIMARY KEY AUTOINCREMENT,
     name text NOT NULL,
     age integer,
     score real DEFAULT 60,
     class_id integer,
     constraint fk_student_class foreign key (class_id) references t_class(id))
     */
    
    // 1. 表名
    NSString *tableName = [GZSQLModelResolver tableName:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    
    // 2. 获取类中的所有变量及变量类型
    NSString *ivarTypeStr = [GZSQLModelResolver columnName4TypeStr:cls];
    
    // 3. 拼sql
    // 获取主键字段
    NSString *primaryKey = [cls primaryKey];
    NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (%@, PRIMARY KEY(%@))", tableName, ivarTypeStr, primaryKey];
    
    // 4. 执行sql
    return [GZSQLTool executeUpdate:sql uid:uid];
}

+ (BOOL)isTableRequireDataMigration:(Class)cls uid:(NSString *)uid
{
    NSArray *sortedIvarNames = [GZSQLModelResolver classSortedIvarNames:cls];
    NSArray *sortedColumnNames = [GZSQLTableResolver tableSortedColumnNames:cls uid:uid];
    
    BOOL equal = [sortedIvarNames isEqualToArray:sortedColumnNames];
    return equal == NO;
}

+ (BOOL)tableDataMigration:(Class)cls uid:(NSString *)uid
{
    NSMutableArray *sqls = [NSMutableArray arrayWithCapacity:0];
    
    // 1. 创建tmp_table
    NSString *oldTableName = [GZSQLModelResolver tableName:cls];
    NSString *tmpTableName = [GZSQLModelResolver tmpTableName:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    NSString *ivarTypeStr = [GZSQLModelResolver columnName4TypeStr:cls];
    NSString *primaryKey = [cls primaryKey];
    
    {
        NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (%@, PRIMARY KEY(%@));", tmpTableName, ivarTypeStr, primaryKey];
        [sqls addObject:sql];
    }
    
    // 2. 向tmp_table中插入old_table的主键值
    {
        NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@ (%@) SELECT %@ FROM %@;", tmpTableName, primaryKey, primaryKey, oldTableName];
        [sqls addObject:sql];
    }
    
    // 3. 根据主键将old_table中的数据更新到tmp_table中
    NSArray *oldColumnNames = [GZSQLTableResolver tableSortedColumnNames:cls uid:uid];
    NSArray *newColumnNmaes = [GZSQLModelResolver classSortedIvarNames:cls];
    
    NSDictionary *newColumn_oldCoumnDict = nil;
    if ([cls respondsToSelector:@selector(newColumnNameFromColumnDict)]) {
        newColumn_oldCoumnDict = [cls newColumnNameFromColumnDict];
    }
    
    for (NSString *newColumnName in newColumnNmaes) {
        NSString *oldColumnName = newColumnName;
        if ([newColumn_oldCoumnDict[newColumnName] length] > 0) {
            oldColumnName = newColumn_oldCoumnDict[newColumnName];
        }
        
        if ((![oldColumnNames containsObject:newColumnName] && ![oldColumnNames containsObject:oldColumnName]) || [newColumnName isEqualToString:primaryKey] ) {
            continue;
        }
        
        
        NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ = (SELECT %@ FROM %@ WHERE %@.%@ = %@.%@);", tmpTableName, newColumnName, oldColumnName, oldTableName, tmpTableName, primaryKey, oldTableName, primaryKey];
        [sqls addObject:sql];
    }
    
    
    // 4. 删除old_table
    {
        NSString *sql = [NSString stringWithFormat:@"DROP TABLE IF EXISTS %@;", oldTableName];
        [sqls addObject:sql];
    }
    
    // 5. tmp_table更名为old_table
    {
        NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ RENAME TO %@;", tmpTableName, oldTableName];
        [sqls addObject:sql];
    }
    
    NSString *excuteSql = [sqls componentsJoinedByString:@""];
    NSLog(@"%@", excuteSql);
    return [GZSQLTool executeStatements:excuteSql uid:uid];
}

+ (BOOL)saveOrUpdateModel:(id)model uid:(NSString *)uid
{
    if (model == nil) {
        NSLog(@"%s 保存的对象不应该是空值", __func__);
        return NO;
    }
    
    Class cls = [model class];
    // 1. 判断表是否存在
    if ([GZSQLTableResolver tableIsExist:cls uid:uid] == NO) {
        [self createTable:cls uid:uid];
    }
    
    // 2. 判断表是否需要进行数据迁移
    if ([self isTableRequireDataMigration:cls uid:uid]) {
        BOOL ok = [self tableDataMigration:cls uid:uid];
        if (ok == NO) {
            NSLog(@"数据迁移失败");
            return NO;
        }
    }
    
    // 3. 判断记录是否存在
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKey:primaryKey];
    NSString *tableName = [GZSQLModelResolver tableName:cls];
    BOOL isExist = NO;
    {
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@ = '%@'", tableName, primaryKey, primaryValue];
        isExist = [[GZSQLTool executeQuery:sql uid:uid] count] > 0 ? YES : NO;
    }
    
    NSMutableArray *ivarAryM = [NSMutableArray arrayWithCapacity:0];
    NSMutableArray *valueAryM = [NSMutableArray arrayWithCapacity:0];
    NSArray *ivarNames = [GZSQLModelResolver classSortedIvarNames:cls];
    
    // 3.1 记录存在，则更新该记录
    if (isExist == YES) {
       // update 表名 set 字段1=字段1值,字段2=字段2的值... where 主键 = '主键值'
        for (NSString *ivarName in ivarNames) {
            id value = [model valueForKey:ivarName];
            NSString *ivarStr = [NSString stringWithFormat:@"%@ = ?", ivarName];
            [ivarAryM addObject:ivarStr];
            value = [self handleArrayOrDictValue:value];
            [valueAryM addObject:value];
        }
        
        NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = ?", tableName, [ivarAryM componentsJoinedByString:@","], primaryKey];
        
        [valueAryM addObject:primaryValue];
        return [GZSQLTool executeUpdate:sql withArgumentsInArray:valueAryM uid:uid];
        
    }
    
    // 3.2 记录不存在，则插入该记录
    // insert into 表名(字段1, 字段2, 字段3) values ('值1', '值2', '值3')
    for (NSString *ivarName in ivarNames) {
        id value = [model valueForKey:ivarName];
        [ivarAryM addObject:@"?"];
        value = [self handleArrayOrDictValue:value];
        [valueAryM addObject:value];
    }
    
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@)", tableName, [ivarNames componentsJoinedByString:@","], [ivarAryM componentsJoinedByString:@","]];
    return [GZSQLTool executeUpdate:sql withArgumentsInArray:valueAryM uid:uid];
    
}

+ (id)handleArrayOrDictValue:(id)value
{
    if ([value isKindOfClass:[NSArray class]] ||
        [value isKindOfClass:[NSDictionary class]] ||
        [value isKindOfClass:[NSSet class]]) {
        NSData *data = [NSJSONSerialization dataWithJSONObject:value options:NSJSONWritingPrettyPrinted error:nil];
        return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }
    
    // 不是字典 or 数组 or 集合 则返回自身
    return value;
}

+ (BOOL)deleteModel:(id)model uid:(NSString *)uid
{
    Class cls = [model class];
    
    NSString *primaryKey = [GZSQLModelResolver modelPrimaryKey:cls];
    if (primaryKey == nil) {
        return NO;
    }
    id primaryValue = [model valueForKey:primaryKey];
    NSString *tableName = [GZSQLModelResolver tableName:cls];
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = '%@'", tableName, primaryKey, primaryValue];
    
    return [GZSQLTool executeUpdate:sql uid:uid];
}

+ (BOOL)deleteClass:(Class)cls condition:(NSString *)condition uid:(NSString *)uid
{
    NSString *tableName = [GZSQLModelResolver tableName:cls];
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@", tableName];
    
    if (condition.length > 0) {
        sql = [NSString stringWithFormat:@"%@ WHERE %@", sql, condition];
    }
    
    return [GZSQLTool executeUpdate:sql uid:uid];
}

+ (NSArray *)queryAllModels:(Class)cls uid:(NSString *)uid
{
    return [self queryClass:cls condition:nil uid:uid];
}

+ (NSArray *)queryClass:(Class)cls condition:(NSString *)condition uid:(NSString *)uid
{
    NSString *tableName = [GZSQLModelResolver tableName:cls];
    NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@", tableName];
    if (condition.length > 0) {
        sql = [NSString stringWithFormat:@"%@ WHERE %@", sql, condition];
    }
    
    NSArray *modelDicts = [GZSQLTool executeQuery:sql uid:uid];
    
    NSMutableArray *models = [NSMutableArray arrayWithCapacity:0];
    NSDictionary *ivarTypeDict = [GZSQLModelResolver classIvarNameTypeDict:cls];
    for (NSDictionary *modelDict  in modelDicts) {
        id model = [[cls alloc] init];
        
        for (NSString *ivarName in modelDict) {
            id obj = [modelDict objectForKey:ivarName];
            if ([obj isKindOfClass:[NSString class]] ? [obj isEqualToString:@"<null>"] :
                [obj isKindOfClass:[NSNull class]]) {
                // 该字段是空值，所以不用赋值
                continue;
            }
            
            NSString *classType = [ivarTypeDict objectForKey:ivarName];
            
            if ([classType isEqualToString:@"NSArray"] ||
                [classType isEqualToString:@"NSMutableArray"] ||
                [classType isEqualToString:@"NSSet"]) {
                
                NSString *jsonString = obj;
                obj = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
                                                      options:kNilOptions
                                                        error:nil];
            
            }
            else if ([classType isEqualToString:@"NSDictionary"] ||
                     [classType isEqualToString:@"NSMutableDictionary"] ||
                     [classType isEqualToString:@"NSMutableSet"]) {
                
                NSString *jsonString = obj;
                obj = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
                                                      options:NSJSONReadingMutableContainers
                                                        error:nil];
            
            }
            
            [model setValue:obj forKey:ivarName];
        }
        
        [models addObject:model];
    }
    
    return models.copy;
}

@end
